// 题目四：CPU 数据通路集成测试（挑战题）
// 模块作用说明：
// 本题将前面设计的ALU（运算执行）、RegFile（数据存储）、Decoder（控制解析）三个模
// 块整合，构建出一个简化的CPU处理器数据通路原型。该系统支持：从指令中解析控制
// 信号、读取寄存器数据、完成运算并将结果写回寄存器，实现基本的“取指-译码-执行-写
// 回”执行流。学生需要在testbench 中使用一组“手写指令”以模拟真实程序的执行。
// 请根据以下接口设计一个顶层模块，整合此前模块：
// 1 module cpu_datapath (
//  2
//  3
//  4
//  5
//  6 );
//  input wire clk,
//  input wire rst,
//  input wire [7:0] instr,
//  // 输入指令
// output wire [7:0] alu_result // 当前指令执行结果（用于观察）
// Listing 4: 微型数据通路顶层模块接口定义
// 模块连接说明：
// • instr 作为输入，送入decoder 模块，生成 op、rs1、rs2；
// • rs1, rs2 用于从 regfile 中读取两个操作数；
// • op, a, b 三者送入 ALU 进行计算；
// 4
// • ALU输出写回到rs1；
// • 每一条指令完整经历一次decode→read→execute → write；

//ALU模块
module alu(
    input wire[3:0] op, //操作码
    input wire[7:0] a,b, //操作数
    output reg [7:0] result,
    output reg zero_flag
);

always @(*) begin
    case(op)
        4'h0: begin  //加法
            result = a+b;
        end
        4'h1: begin //减法
            result = a-b;
        end
        4'h2: begin //按位与
            result = a&b;
        end
        4'h3: begin //按位或
            result = a|b;
        end
        4'h4: begin //按位异或
            result = a^b;
        end
        4'h5: begin //左移一位
            result = a<<1;
        end
        4'h6: begin //逻辑右移一位
            result = a>>1;
        end
        4'h7: begin //算术右移一位(带符号)
        //将a转换为有符号数
            result = $signed (a>>>1) ;
        end
        default: begin
            result = 8'b0;
        end
    endcase

    if(result == 8'b0) begin
        zero_flag = 1'b1;
    end
    else begin
        zero_flag = 1'b0;
    end
end

endmodule


//寄存器堆模块
module regfile(
    input wire clk,//时钟信号
    input wire rst,//复位信号 高电平有效
    input wire we,//写使能信号
    input wire init_en,       // 初始化使能信号
    input wire [1:0] init_addr, // 初始化地址
    input wire [7:0] init_data, // 初始化数据
    input wire[1:0] raddr1, raddr2, waddr, 
    input wire[7:0] wdata,
    output wire[7:0] rdata1,rdata2
);

    reg[7:0] regs[3:0]; //定义4个8位寄存器

    // 同步复位和初始化
    always @(posedge clk) begin
        if(rst) begin // 复位时所有寄存器清零
            regs[0] <= 8'b0;
            regs[1] <= 8'b0;
            regs[2] <= 8'b0;
            regs[3] <= 8'b0;
        end
        else if(init_en) begin // 初始化使能时，将init_data写入init_addr指定的寄存器
            regs[init_addr] <= init_data;
        end
        else if(we && !rst) begin // 正常写操作
            regs[waddr] <= wdata;
        end
    end

    // assign rdata1 = (we&& (raddr1 == waddr)) ? wdata : regs[raddr1];
    // assign rdata2 = (we&& (raddr2 == waddr)) ? wdata : regs[raddr2];

    assign rdata1 = regs[raddr1];
    assign rdata2 = regs[raddr2];
endmodule

//指令译码器模块
module decoder(
    input wire[7:0] instr,
    output wire[3:0] op,
    output wire[1:0]rs1,
    output wire[1:0]rs2
);

    //使用assign语句来提取操作码和寄存器地址
    assign op = instr[7:4];
    assign rs1 = instr[3:2];
    assign rs2 = instr[1:0];
endmodule


module cpu_datapath (
    input wire clk,
    input wire rst,
    input wire [7:0] instr,
    input wire init_en,       
    input wire [1:0] init_addr, 
    input wire [7:0] init_data, 
    output wire [7:0] alu_result // 当前指令执行结果（用于观察）
);
    wire [3:0] op;
    wire [1:0] rs1, rs2;
    wire [7:0] rdata1, rdata2;

    // 实例化decoder模块
    decoder dec (
       .instr(instr),
       .op(op),
       .rs1(rs1),
       .rs2(rs2)
    );

    // 实例化regFile模块
    regfile rf (
       .clk(clk),
       .rst(rst),
       .we(1'b1),  //假设始终允许写回
       .init_en(init_en),
       .init_addr(init_addr),
       .init_data(init_data),
       .raddr1(rs1),
       .raddr2(rs2),
       .waddr(rs1),  //默认写回地址rs1
       .wdata(alu_result),
       .rdata1(rdata1),
       .rdata2(rdata2)
    );

    // 实例化aLU模块
    alu alu_uut (
       .op(op),
       .a(rdata1),
       .b(rdata2),
       .result(alu_result),
       .zero_flag()  // 这里若不需要zero_flag可空着
    );
endmodule

`timescale 1ns/100ps

module cpu_datapath_tb;
    reg clk;
    reg rst;
    reg[7:0] instr;
    wire[7:0] alu_result;
    reg init_en;
    reg [1:0] init_addr;
    reg [7:0] init_data;

    //实例化
    cpu_datapath uut (
       .clk(clk),
       .rst(rst),
       .init_en(init_en),
       .init_addr(init_addr),
       .init_data(init_data),
       .instr(instr),
       .alu_result(alu_result)
    );

    //生成时钟信号
    initial begin
        clk =0;
        forever #5 begin
            clk = ~clk;
        end
    end

    initial begin
        //先复位
        rst = 1;
        init_en = 0;
        @(posedge clk);
        rst = 0;
        @(posedge clk);

        // 设置寄存器r1, r2, r3的初值
        init_en = 1;
        // 初始化r1为5
        init_addr = 2'b01; //reg1   
        init_data = 8'd5;
        @(posedge clk);
        // 初始化r2为3
        init_addr = 2'b10; //reg2
        init_data = 8'd3;
        @(posedge clk);
        // 初始化r3为7
        init_addr = 2'b11; //reg3
        init_data = 8'd7;
        @(posedge clk);
        init_en = 0;
        @(posedge clk);

        // 测试5条指令
        // 加法
        instr = 8'h06;
        @(posedge clk); // 第1周期：指令传入
        @(posedge clk); // 第2周期：执行+写回
        @(posedge clk); // 第3周期：结果稳定
        $display("add r1, r2，alu_result = %d", alu_result);

        // 减法
        instr = 8'h17;
        @(posedge clk);
        @(posedge clk);
        @(posedge clk);
        $display("sub r1,r3，alu_result = %d", alu_result);

        // 异或
        instr = 8'h46;
        @(posedge clk);
        @(posedge clk);
        @(posedge clk);
        @(posedge clk);
        $display("xor r1, r2，alu_result = %d", alu_result);

        // 左移
        instr = 8'h54;
        @(posedge clk);
        @(posedge clk);
        @(posedge clk);
        $display("sll r1，alu_result = %d", alu_result);

        // 右移
        instr = 8'h64;
        @(posedge clk);
        @(posedge clk);
        @(posedge clk);
        $display("srl r1，alu_result = %d", alu_result);

        $stop;

    end

endmodule